package query

import (
	"context"
	"errors"
	"fmt"
	"reflect"
	"regexp"
	"strconv"
	"strings"

	"github.com/louloulin/dataflare/pkg/state/store"
)

// QueryType represents the query type
type QueryType int

const (
	// QueryTypeGet represents a get query
	QueryTypeGet QueryType = iota
	// QueryTypeList represents a list query
	QueryTypeList
	// QueryTypeSearch represents a search query
	QueryTypeSearch
	// QueryTypeAggregate represents an aggregate query
	QueryTypeAggregate
)

// String returns the string representation of the query type
func (t QueryType) String() string {
	switch t {
	case QueryTypeGet:
		return "get"
	case QueryTypeList:
		return "list"
	case QueryTypeSearch:
		return "search"
	case QueryTypeAggregate:
		return "aggregate"
	default:
		return "unknown"
	}
}

// AggregateFunction represents the aggregate function
type AggregateFunction int

const (
	// AggregateFunctionCount represents the count function
	AggregateFunctionCount AggregateFunction = iota
	// AggregateFunctionSum represents the sum function
	AggregateFunctionSum
	// AggregateFunctionAvg represents the average function
	AggregateFunctionAvg
	// AggregateFunctionMin represents the minimum function
	AggregateFunctionMin
	// AggregateFunctionMax represents the maximum function
	AggregateFunctionMax
)

// String returns the string representation of the aggregate function
func (f AggregateFunction) String() string {
	switch f {
	case AggregateFunctionCount:
		return "count"
	case AggregateFunctionSum:
		return "sum"
	case AggregateFunctionAvg:
		return "avg"
	case AggregateFunctionMin:
		return "min"
	case AggregateFunctionMax:
		return "max"
	default:
		return "unknown"
	}
}

// ComparisonOperator represents the comparison operator
type ComparisonOperator int

const (
	// ComparisonOperatorEqual represents the equal operator
	ComparisonOperatorEqual ComparisonOperator = iota
	// ComparisonOperatorNotEqual represents the not equal operator
	ComparisonOperatorNotEqual
	// ComparisonOperatorGreaterThan represents the greater than operator
	ComparisonOperatorGreaterThan
	// ComparisonOperatorGreaterThanOrEqual represents the greater than or equal operator
	ComparisonOperatorGreaterThanOrEqual
	// ComparisonOperatorLessThan represents the less than operator
	ComparisonOperatorLessThan
	// ComparisonOperatorLessThanOrEqual represents the less than or equal operator
	ComparisonOperatorLessThanOrEqual
	// ComparisonOperatorIn represents the in operator
	ComparisonOperatorIn
	// ComparisonOperatorNotIn represents the not in operator
	ComparisonOperatorNotIn
	// ComparisonOperatorLike represents the like operator
	ComparisonOperatorLike
	// ComparisonOperatorNotLike represents the not like operator
	ComparisonOperatorNotLike
)

// String returns the string representation of the comparison operator
func (o ComparisonOperator) String() string {
	switch o {
	case ComparisonOperatorEqual:
		return "="
	case ComparisonOperatorNotEqual:
		return "!="
	case ComparisonOperatorGreaterThan:
		return ">"
	case ComparisonOperatorGreaterThanOrEqual:
		return ">="
	case ComparisonOperatorLessThan:
		return "<"
	case ComparisonOperatorLessThanOrEqual:
		return "<="
	case ComparisonOperatorIn:
		return "in"
	case ComparisonOperatorNotIn:
		return "not in"
	case ComparisonOperatorLike:
		return "like"
	case ComparisonOperatorNotLike:
		return "not like"
	default:
		return "unknown"
	}
}

// LogicalOperator represents the logical operator
type LogicalOperator int

const (
	// LogicalOperatorAnd represents the and operator
	LogicalOperatorAnd LogicalOperator = iota
	// LogicalOperatorOr represents the or operator
	LogicalOperatorOr
	// LogicalOperatorNot represents the not operator
	LogicalOperatorNot
)

// String returns the string representation of the logical operator
func (o LogicalOperator) String() string {
	switch o {
	case LogicalOperatorAnd:
		return "and"
	case LogicalOperatorOr:
		return "or"
	case LogicalOperatorNot:
		return "not"
	default:
		return "unknown"
	}
}

// SortOrder represents the sort order
type SortOrder int

const (
	// SortOrderAscending represents the ascending order
	SortOrderAscending SortOrder = iota
	// SortOrderDescending represents the descending order
	SortOrderDescending
)

// String returns the string representation of the sort order
func (o SortOrder) String() string {
	switch o {
	case SortOrderAscending:
		return "asc"
	case SortOrderDescending:
		return "desc"
	default:
		return "unknown"
	}
}

// Query represents a query
type Query struct {
	// Type is the query type
	Type QueryType
	// State is the state name
	State string
	// Key is the key
	Key string
	// Condition is the condition
	Condition *Condition
	// Aggregate is the aggregate function
	Aggregate *Aggregate
	// Sort is the sort
	Sort *Sort
	// Limit is the limit
	Limit int
	// Offset is the offset
	Offset int
}

// Condition represents a condition
type Condition struct {
	// Operator is the logical operator
	Operator LogicalOperator
	// Left is the left condition
	Left *Condition
	// Right is the right condition
	Right *Condition
	// Field is the field
	Field string
	// ComparisonOperator is the comparison operator
	ComparisonOperator ComparisonOperator
	// Value is the value
	Value interface{}
}

// Aggregate represents an aggregate
type Aggregate struct {
	// Function is the aggregate function
	Function AggregateFunction
	// Field is the field
	Field string
	// Alias is the alias
	Alias string
}

// Sort represents a sort
type Sort struct {
	// Field is the field
	Field string
	// Order is the sort order
	Order SortOrder
}

// QueryResult represents a query result
type QueryResult struct {
	// Type is the query type
	Type QueryType
	// Records is the records
	Records []map[string]interface{}
	// AggregateValue is the aggregate value
	AggregateValue interface{}
	// Count is the count
	Count int
}

// QueryEngine represents the query engine
type QueryEngine struct {
	// store is the state store
	store store.StateStore
}

// NewQueryEngine creates a new query engine
func NewQueryEngine(store store.StateStore) *QueryEngine {
	return &QueryEngine{
		store: store,
	}
}

// Execute executes a query
func (e *QueryEngine) Execute(ctx context.Context, query *Query) (*QueryResult, error) {
	// Check the query type
	switch query.Type {
	case QueryTypeGet:
		return e.executeGet(ctx, query)
	case QueryTypeList:
		return e.executeList(ctx, query)
	case QueryTypeSearch:
		return e.executeSearch(ctx, query)
	case QueryTypeAggregate:
		return e.executeAggregate(ctx, query)
	default:
		return nil, fmt.Errorf("unknown query type: %s", query.Type)
	}
}

// executeGet executes a get query
func (e *QueryEngine) executeGet(ctx context.Context, query *Query) (*QueryResult, error) {
	// Get the state
	state, err := e.store.GetState(ctx, query.State)
	if err != nil {
		return nil, fmt.Errorf("failed to get state: %w", err)
	}

	// Check the state type
	kvState, ok := state.(store.KVState)
	if !ok {
		return nil, errors.New("state is not a KV state")
	}

	// Get the value
	value, err := kvState.Get(ctx, query.Key)
	if err != nil {
		return nil, fmt.Errorf("failed to get value: %w", err)
	}

	// Create the result
	result := &QueryResult{
		Type:    QueryTypeGet,
		Records: make([]map[string]interface{}, 0),
	}

	// Add the record
	record := make(map[string]interface{})
	record["key"] = query.Key
	record["value"] = value
	result.Records = append(result.Records, record)
	result.Count = 1

	return result, nil
}

// executeList executes a list query
func (e *QueryEngine) executeList(ctx context.Context, query *Query) (*QueryResult, error) {
	// Get the state
	state, err := e.store.GetState(ctx, query.State)
	if err != nil {
		return nil, fmt.Errorf("failed to get state: %w", err)
	}

	// Check the state type
	kvState, ok := state.(store.KVState)
	if !ok {
		return nil, errors.New("state is not a KV state")
	}

	// Get all keys
	keys, err := kvState.Keys(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to get keys: %w", err)
	}

	// Create the result
	result := &QueryResult{
		Type:    QueryTypeList,
		Records: make([]map[string]interface{}, 0),
	}

	// Apply the limit and offset
	start := query.Offset
	end := len(keys)
	if query.Limit > 0 {
		end = start + query.Limit
		if end > len(keys) {
			end = len(keys)
		}
	}

	// Get the values
	for i := start; i < end; i++ {
		key := keys[i]
		value, err := kvState.Get(ctx, key)
		if err != nil {
			return nil, fmt.Errorf("failed to get value: %w", err)
		}

		// Add the record
		record := make(map[string]interface{})
		record["key"] = key
		record["value"] = value
		result.Records = append(result.Records, record)
	}

	result.Count = len(result.Records)

	return result, nil
}

// executeSearch executes a search query
func (e *QueryEngine) executeSearch(ctx context.Context, query *Query) (*QueryResult, error) {
	// Get the state
	state, err := e.store.GetState(ctx, query.State)
	if err != nil {
		return nil, fmt.Errorf("failed to get state: %w", err)
	}

	// Check the state type
	kvState, ok := state.(store.KVState)
	if !ok {
		return nil, errors.New("state is not a KV state")
	}

	// Get all keys
	keys, err := kvState.Keys(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to get keys: %w", err)
	}

	// Create the result
	result := &QueryResult{
		Type:    QueryTypeSearch,
		Records: make([]map[string]interface{}, 0),
	}

	// Get the values and apply the condition
	for _, key := range keys {
		value, err := kvState.Get(ctx, key)
		if err != nil {
			return nil, fmt.Errorf("failed to get value: %w", err)
		}

		// Create the record
		record := make(map[string]interface{})
		record["key"] = key
		record["value"] = value

		// Apply the condition
		if query.Condition != nil {
			match, err := e.evaluateCondition(query.Condition, record)
			if err != nil {
				return nil, fmt.Errorf("failed to evaluate condition: %w", err)
			}
			if !match {
				continue
			}
		}

		// Add the record
		result.Records = append(result.Records, record)
	}

	// Apply the sort
	if query.Sort != nil {
		e.sortRecords(result.Records, query.Sort)
	}

	// Apply the limit and offset
	start := query.Offset
	end := len(result.Records)
	if query.Limit > 0 {
		end = start + query.Limit
		if end > len(result.Records) {
			end = len(result.Records)
		}
	}
	result.Records = result.Records[start:end]
	result.Count = len(result.Records)

	return result, nil
}

// executeAggregate executes an aggregate query
func (e *QueryEngine) executeAggregate(ctx context.Context, query *Query) (*QueryResult, error) {
	// Get the state
	state, err := e.store.GetState(ctx, query.State)
	if err != nil {
		return nil, fmt.Errorf("failed to get state: %w", err)
	}

	// Check the state type
	kvState, ok := state.(store.KVState)
	if !ok {
		return nil, errors.New("state is not a KV state")
	}

	// Get all keys
	keys, err := kvState.Keys(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to get keys: %w", err)
	}

	// Create the result
	result := &QueryResult{
		Type:    QueryTypeAggregate,
		Records: make([]map[string]interface{}, 0),
	}

	// Get the values and apply the condition
	filteredRecords := make([]map[string]interface{}, 0)
	for _, key := range keys {
		value, err := kvState.Get(ctx, key)
		if err != nil {
			return nil, fmt.Errorf("failed to get value: %w", err)
		}

		// Create the record
		record := make(map[string]interface{})
		record["key"] = key
		record["value"] = value

		// Apply the condition
		if query.Condition != nil {
			match, err := e.evaluateCondition(query.Condition, record)
			if err != nil {
				return nil, fmt.Errorf("failed to evaluate condition: %w", err)
			}
			if !match {
				continue
			}
		}

		// Add the record
		filteredRecords = append(filteredRecords, record)
	}

	// Apply the aggregate function
	if query.Aggregate != nil {
		aggregateValue, err := e.calculateAggregate(query.Aggregate, filteredRecords)
		if err != nil {
			return nil, fmt.Errorf("failed to calculate aggregate: %w", err)
		}
		result.AggregateValue = aggregateValue
	}

	result.Count = len(filteredRecords)

	return result, nil
}

// evaluateCondition evaluates a condition
func (e *QueryEngine) evaluateCondition(condition *Condition, record map[string]interface{}) (bool, error) {
	// Check if it's a compound condition
	if condition.Left != nil && condition.Right != nil {
		// Evaluate the left condition
		leftResult, err := e.evaluateCondition(condition.Left, record)
		if err != nil {
			return false, fmt.Errorf("failed to evaluate left condition: %w", err)
		}

		// Evaluate the right condition
		rightResult, err := e.evaluateCondition(condition.Right, record)
		if err != nil {
			return false, fmt.Errorf("failed to evaluate right condition: %w", err)
		}

		// Apply the logical operator
		switch condition.Operator {
		case LogicalOperatorAnd:
			return leftResult && rightResult, nil
		case LogicalOperatorOr:
			return leftResult || rightResult, nil
		default:
			return false, fmt.Errorf("unknown logical operator: %s", condition.Operator)
		}
	} else if condition.Left != nil {
		// It's a not condition
		if condition.Operator != LogicalOperatorNot {
			return false, errors.New("expected not operator for single condition")
		}

		// Evaluate the condition
		result, err := e.evaluateCondition(condition.Left, record)
		if err != nil {
			return false, fmt.Errorf("failed to evaluate condition: %w", err)
		}

		// Apply the not operator
		return !result, nil
	}

	// It's a simple condition
	// Get the field value
	fieldValue, ok := record[condition.Field]
	if !ok {
		return false, fmt.Errorf("field not found: %s", condition.Field)
	}

	// Apply the comparison operator
	switch condition.ComparisonOperator {
	case ComparisonOperatorEqual:
		return reflect.DeepEqual(fieldValue, condition.Value), nil
	case ComparisonOperatorNotEqual:
		return !reflect.DeepEqual(fieldValue, condition.Value), nil
	case ComparisonOperatorGreaterThan:
		return e.compareValues(fieldValue, condition.Value) > 0, nil
	case ComparisonOperatorGreaterThanOrEqual:
		return e.compareValues(fieldValue, condition.Value) >= 0, nil
	case ComparisonOperatorLessThan:
		return e.compareValues(fieldValue, condition.Value) < 0, nil
	case ComparisonOperatorLessThanOrEqual:
		return e.compareValues(fieldValue, condition.Value) <= 0, nil
	case ComparisonOperatorIn:
		return e.isValueInList(fieldValue, condition.Value), nil
	case ComparisonOperatorNotIn:
		return !e.isValueInList(fieldValue, condition.Value), nil
	case ComparisonOperatorLike:
		return e.isValueLike(fieldValue, condition.Value), nil
	case ComparisonOperatorNotLike:
		return !e.isValueLike(fieldValue, condition.Value), nil
	default:
		return false, fmt.Errorf("unknown comparison operator: %s", condition.ComparisonOperator)
	}
}

// compareValues compares two values
func (e *QueryEngine) compareValues(a, b interface{}) int {
	// Convert to comparable types
	aValue := e.convertToComparable(a)
	bValue := e.convertToComparable(b)

	// Compare the values
	switch aValue.(type) {
	case int64:
		return int(aValue.(int64) - bValue.(int64))
	case float64:
		diff := aValue.(float64) - bValue.(float64)
		if diff < 0 {
			return -1
		} else if diff > 0 {
			return 1
		}
		return 0
	case string:
		return strings.Compare(aValue.(string), bValue.(string))
	default:
		return 0
	}
}

// convertToComparable converts a value to a comparable type
func (e *QueryEngine) convertToComparable(value interface{}) interface{} {
	switch v := value.(type) {
	case int:
		return int64(v)
	case int8:
		return int64(v)
	case int16:
		return int64(v)
	case int32:
		return int64(v)
	case int64:
		return v
	case uint:
		return int64(v)
	case uint8:
		return int64(v)
	case uint16:
		return int64(v)
	case uint32:
		return int64(v)
	case uint64:
		return int64(v)
	case float32:
		return float64(v)
	case float64:
		return v
	case string:
		// Try to convert to a number
		if i, err := strconv.ParseInt(v, 10, 64); err == nil {
			return i
		}
		if f, err := strconv.ParseFloat(v, 64); err == nil {
			return f
		}
		return v
	case []byte:
		return string(v)
	default:
		return fmt.Sprintf("%v", v)
	}
}

// isValueInList checks if a value is in a list
func (e *QueryEngine) isValueInList(value, list interface{}) bool {
	// Convert the list to a slice
	listValue := reflect.ValueOf(list)
	if listValue.Kind() != reflect.Slice {
		return false
	}

	// Check if the value is in the list
	for i := 0; i < listValue.Len(); i++ {
		if reflect.DeepEqual(value, listValue.Index(i).Interface()) {
			return true
		}
	}

	return false
}

// isValueLike checks if a value is like a pattern
func (e *QueryEngine) isValueLike(value, pattern interface{}) bool {
	// Convert to strings
	valueStr := fmt.Sprintf("%v", value)
	patternStr := fmt.Sprintf("%v", pattern)

	// Convert SQL LIKE pattern to regex
	patternStr = strings.ReplaceAll(patternStr, "%", ".*")
	patternStr = strings.ReplaceAll(patternStr, "_", ".")
	patternStr = "^" + patternStr + "$"

	// Create the regex
	regex, err := regexp.Compile(patternStr)
	if err != nil {
		return false
	}

	// Check if the value matches the pattern
	return regex.MatchString(valueStr)
}

// sortRecords sorts records
func (e *QueryEngine) sortRecords(records []map[string]interface{}, sort *Sort) {
	// Sort the records
	if sort.Order == SortOrderAscending {
		// Sort in ascending order
		for i := 0; i < len(records); i++ {
			for j := i + 1; j < len(records); j++ {
				if e.compareValues(records[i][sort.Field], records[j][sort.Field]) > 0 {
					records[i], records[j] = records[j], records[i]
				}
			}
		}
	} else {
		// Sort in descending order
		for i := 0; i < len(records); i++ {
			for j := i + 1; j < len(records); j++ {
				if e.compareValues(records[i][sort.Field], records[j][sort.Field]) < 0 {
					records[i], records[j] = records[j], records[i]
				}
			}
		}
	}
}

// calculateAggregate calculates an aggregate
func (e *QueryEngine) calculateAggregate(aggregate *Aggregate, records []map[string]interface{}) (interface{}, error) {
	// Check the aggregate function
	switch aggregate.Function {
	case AggregateFunctionCount:
		return len(records), nil
	case AggregateFunctionSum:
		return e.calculateSum(aggregate.Field, records)
	case AggregateFunctionAvg:
		return e.calculateAvg(aggregate.Field, records)
	case AggregateFunctionMin:
		return e.calculateMin(aggregate.Field, records)
	case AggregateFunctionMax:
		return e.calculateMax(aggregate.Field, records)
	default:
		return nil, fmt.Errorf("unknown aggregate function: %s", aggregate.Function)
	}
}

// calculateSum calculates the sum
func (e *QueryEngine) calculateSum(field string, records []map[string]interface{}) (interface{}, error) {
	// Initialize the sum
	var sum float64

	// Calculate the sum
	for _, record := range records {
		// Get the field value
		fieldValue, ok := record[field]
		if !ok {
			continue
		}

		// Convert to a number
		value := e.convertToComparable(fieldValue)
		switch v := value.(type) {
		case int64:
			sum += float64(v)
		case float64:
			sum += v
		default:
			continue
		}
	}

	return sum, nil
}

// calculateAvg calculates the average
func (e *QueryEngine) calculateAvg(field string, records []map[string]interface{}) (interface{}, error) {
	// Calculate the sum
	sum, err := e.calculateSum(field, records)
	if err != nil {
		return nil, fmt.Errorf("failed to calculate sum: %w", err)
	}

	// Calculate the average
	if len(records) == 0 {
		return 0.0, nil
	}
	return sum.(float64) / float64(len(records)), nil
}

// calculateMin calculates the minimum
func (e *QueryEngine) calculateMin(field string, records []map[string]interface{}) (interface{}, error) {
	// Initialize the minimum
	var min interface{}
	var minSet bool

	// Calculate the minimum
	for _, record := range records {
		// Get the field value
		fieldValue, ok := record[field]
		if !ok {
			continue
		}

		// Convert to a comparable type
		value := e.convertToComparable(fieldValue)

		// Update the minimum
		if !minSet || e.compareValues(value, min) < 0 {
			min = value
			minSet = true
		}
	}

	if !minSet {
		return nil, errors.New("no values found")
	}
	return min, nil
}

// calculateMax calculates the maximum
func (e *QueryEngine) calculateMax(field string, records []map[string]interface{}) (interface{}, error) {
	// Initialize the maximum
	var max interface{}
	var maxSet bool

	// Calculate the maximum
	for _, record := range records {
		// Get the field value
		fieldValue, ok := record[field]
		if !ok {
			continue
		}

		// Convert to a comparable type
		value := e.convertToComparable(fieldValue)

		// Update the maximum
		if !maxSet || e.compareValues(value, max) > 0 {
			max = value
			maxSet = true
		}
	}

	if !maxSet {
		return nil, errors.New("no values found")
	}
	return max, nil
}
